
/*
 * Copyright (c) 2010 Sergey N Frolov
 * 
 * See LICENSE file
 */

#include "ngx_javascript.h"

/*
 * JavaScript execute
 */
JSBool ngx_javascript_execute_script(JSContext *jcx, JSObject *obj, const char *fileName, jsval *rval) {
	ngx_javascript_client_t *client;
	jsval result;
	JSObject *global = NULL;
	JSObject *global_scripts = NULL;
	JSObject *script_object = NULL;
	JSScript *script = NULL;
	JSBool ok;
	
	client = JS_GetContextPrivate(jcx);
	if (client == NULL) return ngx_javascript_private_error(jcx);
	global = JS_GetGlobalObject(jcx);
	if (global == NULL) return ngx_javascript_private_error(jcx);
	if (JS_GetProperty(jcx, global, "$scripts", &result) == JS_TRUE && JSVAL_IS_OBJECT(result) && !JSVAL_IS_NULL(result)) {
		global_scripts = JSVAL_TO_OBJECT(result);
	} else {
		global_scripts = JS_NewObject(jcx, NULL, NULL, NULL);
		if (global_scripts == NULL) return JS_FALSE;
		JS_DefineProperty(jcx, obj, "$scripts", OBJECT_TO_JSVAL(global_scripts), NULL, NULL, JSPROP_READONLY|JSPROP_PERMANENT);
	}
	
	if (JS_GetProperty(jcx, global_scripts, fileName, &result) == JS_TRUE && JSVAL_IS_OBJECT(result)) {
		if (JSVAL_IS_NULL(result)) {
			return ngx_javascript_error(jcx, "Recursion");
		} else {
			script_object = JSVAL_TO_OBJECT(result);
			script = JS_GetPrivate(jcx, script_object);
			if (script != NULL) {
				JS_DefineProperty(jcx, global_scripts, fileName, JSVAL_NULL, NULL, NULL, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);
				ok = JS_ExecuteScript(jcx, obj, script, rval);
				JS_DefineProperty(jcx, global_scripts, fileName, result, NULL, NULL, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);
				return ok;
			}
		}
	}
	
	if (1) {
		struct stat st;
		size_t sizeb = -1;
		size_t sizec = -1;
		char *fileb = NULL;
		jschar *filec = NULL;
		int fd = open(fileName, O_RDONLY);
		if (fd < 0) goto error_0;
		if (fstat(fd, &st) < 0) goto error_0;
		sizeb = st.st_size;
		fileb = ngx_javascript_malloc(sizeb);
		if (fileb == NULL) goto error_0;
		if (read(fd, fileb, sizeb) != sizeb) goto error_0;
		close(fd); fd = -1;
		if (JS_DecodeBytes(jcx, fileb, sizeb, filec, &sizec) == JS_FALSE) goto error_0;
		filec = ngx_javascript_malloc(sizec * sizeof(jschar));
		if (filec == NULL) goto error_0;
		if (JS_DecodeBytes(jcx, fileb, sizeb, filec, &sizec) == JS_FALSE) goto error_0;
		ngx_javascript_free(fileb); fileb = NULL;
		script = JS_CompileUCScript(jcx, obj, filec, sizec, fileName, 1);
		if (script == NULL) goto error_0;
		script_object = JS_NewScriptObject(jcx, script);
		JS_DefineProperty(jcx, global_scripts, fileName, JSVAL_NULL, NULL, NULL, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);
		ok = JS_ExecuteScript(jcx, obj, script, rval);
		if (script_object != NULL)
			JS_DefineProperty(jcx, global_scripts, fileName, OBJECT_TO_JSVAL(script_object), NULL, NULL, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);
		return ok;
		error_0:
		if (fd >= 0) close(fd);
		if (fileb != NULL) ngx_javascript_free(fileb);
		if (filec != NULL) ngx_javascript_free(filec);
		JS_ReportError(jcx, "%s: Load failed", fileName);
		return JS_FALSE;
	}
	
}

/*
 * JavaScript load handler
 */
JSBool ngx_javascript_global_load(JSContext *jcx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
	JSBool ok;
	char *filename;
	char realname[PATH_MAX];
	ngx_javascript_client_t *client = JS_GetContextPrivate(jcx);
	if (client == NULL) return ngx_javascript_private_error(jcx);
	if (JS_ConvertArguments(jcx, argc, argv, "s", &filename) == JS_FALSE) return JS_FALSE;
	if (ngx_javascript_realpath(client->worker, filename, realname) == NULL) return ngx_javascript_error(jcx, "Load failed");
	if (ngx_javascript_check_basedir_realpath(jcx, realname) == JS_FALSE) return ngx_javascript_error(jcx, "Load failed");
	ok = ngx_javascript_execute_script(jcx, obj, realname, rval);
	if (*rval == JSVAL_VOID) *rval = JSVAL_TRUE;
	return ok;
}
